package com.xyy.saas.payment.util;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.xyy.saas.common.util.CommonHttpClientUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

import java.io.*;
import java.net.URISyntaxException;
import java.util.*;

/**
 * @Author: wuqiongda
 * @Date: 2019/11/12 14:58
 */
public class HttpClientUtil implements InitializingBean {


    private static Logger LOGGER = LoggerFactory.getLogger(HttpClientUtil.class);

    private static String EMPTY_STR = "";
    public static final String UTF8 = "UTF-8";
    public static volatile boolean isClosed = false;

    private static String CONNECTION_TIMEOUT = "http.connectTimeout";
    private static String SOCKET_TIMEOUT = "http.socketTimeout";
    private static String REQUEST_TIMEOUT = "http.requestTimeout";
    private static String TOTAL_POOL = "http.totalPool";

    public static int maxTotalPool = Integer.parseInt(ApolloUtil.getProperty(TOTAL_POOL));
    public static int MAX_CONNECTION_TIMEOUT = Integer.parseInt(ApolloUtil.getProperty(CONNECTION_TIMEOUT));
    public static int MAX_SOCKET_TIMEOUT = Integer.parseInt(ApolloUtil.getProperty(SOCKET_TIMEOUT));
    public static int RequestTimeout = Integer.parseInt(ApolloUtil.getProperty(REQUEST_TIMEOUT));

    private static RequestConfig requestConfig;
    private static HttpClientBuilder httpClientBuilder;
    private static PoolingHttpClientConnectionManager poolConnManager;
    private static RequestConfig.Builder configBuilder = RequestConfig.custom();
    static {
        // 设置连接池
        poolConnManager = new PoolingHttpClientConnectionManager();
        //设置连接池大小
        poolConnManager.setMaxTotal(maxTotalPool);
        poolConnManager.setDefaultMaxPerRoute(maxTotalPool);

        // 设置连接超时
        configBuilder.setConnectTimeout(MAX_CONNECTION_TIMEOUT);
        // 设置读取超时
        configBuilder.setSocketTimeout(MAX_SOCKET_TIMEOUT);
        // 设置从连接池获取连接实例的超时
        configBuilder.setConnectionRequestTimeout(RequestTimeout);
        requestConfig = configBuilder.build();
        //重试三次
        DefaultHttpRequestRetryHandler dhr = new DefaultHttpRequestRetryHandler(3,true);
        httpClientBuilder = HttpClients.custom().setConnectionManager(poolConnManager).setDefaultRequestConfig(requestConfig).setRetryHandler(dhr);
    }
    /**
     * 从http连接池里获取客户端实例
     * @return httpClient
     */
    public static CloseableHttpClient getHttpClient() {
        return httpClientBuilder.build();
    }

    public static String uploadFile(File file) {
        try {
            HttpPost httpPost = new HttpPost("https://ykq-h5.ybm100.com/storeapp/ownEntry/uploadFile");
            httpPost.setEntity(MultipartEntityBuilder.create().addPart("file", new FileBody(file)).build());
            return (String) ((Map) JSONUtils.getValueFromJson(getResult(httpPost), "result")).get("url");
        }
        catch(Exception ex) {
            LOGGER.error(ex.getMessage() ,ex);
        }

        return StringUtils.EMPTY;
    }

    /**
     * 关闭连接池资源
     */
    public static void closePool() {
        if( !isClosed ){
            isClosed = true;
            poolConnManager.close();
        }
    }

    /**
     * @param url
     * @return
     */
    public static String httpGetRequest(String url) {
        HttpGet httpGet = new HttpGet(url);
        return getResult(httpGet);
    }

    public static String httpGetRequest(String url, Map<String, Object> params) throws URISyntaxException {
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);
        ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        ub.setParameters(pairs);
        HttpGet httpGet = new HttpGet(ub.build());
        return getResult(httpGet);
    }

    public static String httpGetRequest(String url, Map<String, Object> headers, Map<String, Object> params)
            throws URISyntaxException {
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);
        ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        ub.setParameters(pairs);
        HttpGet httpGet = new HttpGet(ub.build());
        for (Map.Entry<String, Object> param : headers.entrySet()) {
            httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
        }
        return getResult(httpGet);
    }

    public static String httpPostRequest(String url) {
        HttpPost httpPost = new HttpPost(url);
        return getResult(httpPost);
    }

    public static String httpPostRequest(String url,String json) {
        HttpPost httpPost = new HttpPost(url);
        //解决中文乱码问题
        StringEntity entity = new StringEntity(json,"utf-8");
        entity.setContentEncoding("UTF-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        return getResult(httpPost);
    }

    public static String httpPostRequest(String url,String json, String contentType) {
        HttpPost httpPost = new HttpPost(url);
        //解决中文乱码问题
        StringEntity entity = new StringEntity(json,"utf-8");
        entity.setContentEncoding("UTF-8");
        entity.setContentType(contentType);
        httpPost.setEntity(entity);
        return getResult(httpPost);
    }

    public static String httpPostRequest(String url, Map<String, Object> params) throws UnsupportedEncodingException {
        HttpPost httpPost = new HttpPost(url);
        ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF8));
        return getResult(httpPost);
    }

    public static String httpPostRequest(String url, Map<String, Object> headers, Map<String, Object> params)
            throws UnsupportedEncodingException {
        HttpPost httpPost = new HttpPost(url);
        for (Map.Entry<String, Object> param : headers.entrySet()) {
            httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
        }
        ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF8));

        return getResult(httpPost);
    }

    private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params) {
        ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
        for (Map.Entry<String, Object> param : params.entrySet()) {
            pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
        }
        return pairs;
    }

    /**
     * 处理Http请求
     */
    private static String getResult(HttpRequestBase request) {
        CloseableHttpClient httpClient = getHttpClient();

        try {
            CloseableHttpResponse response = httpClient.execute(request);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                // long len = entity.getContentLength();// -1 表示长度未知
                String result = EntityUtils.toString(entity, "UTF-8");
                response.close();
                return result;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return EMPTY_STR;
    }

    public static byte[] getStream(String url){
        HttpGet httpGet = new HttpGet(url);
        CloseableHttpClient httpClient = getHttpClient();
        try {
            CloseableHttpResponse response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream inputStream = entity.getContent();
                byte[] bytes = readInputStream(inputStream);
                response.close();
                return bytes;
            }
        } catch (Exception e) {
        }
        return null;
    }

    public static byte[] readInputStream(InputStream inStream) throws Exception {
        try {
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outStream.write(buffer, 0, len);
            }
            return outStream.toByteArray();
        } catch (Exception e) {
            return null;
        } finally {
            inStream.close();
        }
    }
    public static String xyyHttpPostRequest(String url, Map<String, Object> params) throws Exception {
       return CommonHttpClientUtil.httpPost(url, params, requestConfig);
    }

    public static String xyyHttpGetRequest(String url) throws Exception {
        return CommonHttpClientUtil.httpGet(url, new HashMap<>(2), requestConfig);
    }

    public static String xyyHttpPostRequest(String url,String json, String contentType) throws Exception {
        Map<String, String> headerMap = new HashMap<>(5);
        headerMap.put("content-encoding","UTF-8");
        headerMap.put("content-type", contentType);

        try {
            return CommonHttpClientUtil.httpPost(url, json, headerMap, requestConfig);
        }
        // NoHttpResponseException 重试一次
        catch(NoHttpResponseException ex) {
            LOGGER.warn("Retry due to NoHttpResponseException#xyyHttpPostRequest1:" + ex.getMessage(), ex);
            return CommonHttpClientUtil.httpPost(url, json, headerMap, requestConfig);
        }
    }

    public static String xyyHttpPostRequest(String url,String json, String contentType, Map<String, String> header) throws Exception {
        Map<String, String> headerMap = new HashMap<>(5);
        headerMap.put("content-encoding","UTF-8");
        headerMap.put("content-type", contentType);
        headerMap.putAll(header);

        try {
            return CommonHttpClientUtil.httpPost(url, json, headerMap, requestConfig);
        }
        // NoHttpResponseException 重试一次
        catch(NoHttpResponseException ex) {
            LOGGER.warn("Retry due to NoHttpResponseException#xyyHttpPostRequest2:" + ex.getMessage(), ex);
            return CommonHttpClientUtil.httpPost(url, json, headerMap, requestConfig);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Set set = new HashSet();
        set.add(CONNECTION_TIMEOUT);
        set.add(SOCKET_TIMEOUT);
        set.add(REQUEST_TIMEOUT);
        //动态监听配置变化
        Config config = ConfigService.getAppConfig();
        config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                int connectionTimeOut = Integer.parseInt(ApolloUtil.getProperty(CONNECTION_TIMEOUT));
                int socketTimeOut = Integer.parseInt(ApolloUtil.getProperty(SOCKET_TIMEOUT));
                int requestTimeOut = Integer.parseInt(ApolloUtil.getProperty(REQUEST_TIMEOUT));
                // 设置连接超时
                configBuilder.setConnectTimeout(connectionTimeOut);
                // 设置读取超时
                configBuilder.setSocketTimeout(socketTimeOut);
                // 设置从连接池获取连接实例的超时
                configBuilder.setConnectionRequestTimeout(requestTimeOut);
                requestConfig = configBuilder.build();
            }
        }, set);
    }
}
